Main Menu and State Management
The sample consists of
multiple text menu screens and a game play screen that consists of
moving the player’s gamertag around. Depending on the current state of
the game, you will want to display a different menu and update a
different set of logic. To do this, use a simple enumeration that tracks
the game’s current state.
Outside the Game class but within the same file and namespace, add the following enumeration:
// Different states the game can be in
public enum GameState { MainMenu, CreateSession, FindSession, GameLobby, PlayingGame };
As you can see from the GameState
enumeration, the sample game contains five different states. As you
progress through this article, you implement the update and display
logic for each of these.
During the course of the game,
you will want to display some text for a short period of time. Use this
for a number of notifications including when a player joins or leaves a
session. Use a new structure that contains the message and the time
remaining to display the text. Add the following structure to your game:
// Structure used to display game messages over a TimeSpan
public struct DisplayMessage
{
public string Message;
public TimeSpan DisplayTime;
public DisplayMessage(string message, TimeSpan displayTime)
{
Message = message;
DisplayTime = displayTime;
}
}
The Game class needs a
few member variables. You need to hold the current state that you
created an enumeration for and you need a list of the game messages to
be displayed. Add the following member variables to your Game class:
// Current state our game is in we start at the main menu
GameState gameState = GameState.MainMenu;
// The messages to display on the screen
// We will use this to show events that occur like
// a player joining a session
List<DisplayMessage> gameMessages = new List<DisplayMessage>();
SpriteFont spriteFont;
GamePadState currentGamePadState;
GamePadState lastGamePadState;
Random random = new Random();
In addition to the gameState and gameMessages, create variables for the SpriteFont you will use, the GamePadState for the current and last frame, and a new Random generator that you will use within the game.
In your game’s LoadContent method, load the SpriteFont you plan to use for the text in the sample. Create a new SpriteFont in the content pipeline and set the text size to 24. Add the following to the LoadContent method:
spriteFont = Content.Load<SpriteFont>("SpriteFont1");
Add the following code to your game’s Update method:
// If there is no user signed in after 5 seconds
// then we show the sign in
if (gameTime.TotalGameTime.Seconds > 5)
{
if (Gamer.SignedInGamers.Count == 0 &&
!Guide.IsVisible)
Guide.ShowSignIn(1, false);
}
// Store the current GamePadState
currentGamePadState = GamePad.GetState(PlayerIndex.One);
// Switch to determine which update method to call
// based on the current game state
switch (gameState)
{
case GameState.MainMenu:
MainMenuUpdate();
break;
}
// Store the game pad state for next frame
lastGamePadState = currentGamePadState;
// Update the DisplayTime of the current display message
if (gameMessages.Count > 0)
{
DisplayMessage currentMessage = gameMessages[0];
currentMessage.DisplayTime -= gameTime.ElapsedGameTime;
// Remove the message if the time is up
if (currentMessage.DisplayTime <= TimeSpan.Zero)
{
gameMessages.RemoveAt(0);
}
else
{
gameMessages[0] = currentMessage;
}
}
Check whether any user is currently signed in. If a user is not signed in after 5 seconds, call Guide.ShowSignIn,
which displays the sign-in dialog in the guide. Use a switch statement
to call the appropriate update method depending on the current gameState value. Right now, you have only the MainMenuUpdate method, but as you progress through the article, you add more for each of the different GameState values. The last part of the Update method loops over the gameMessages to determine whether any of them need to be removed so they are no longer displayed.
Add the MainMenuUpdate method that will handle user input and logic when our sample is at the main menu. Add the following method to your Game class:
// Update method for the MainMenu GameState
private void MainMenuUpdate()
{
// Exit the game
if (ButtonPressed(Buttons.Back))
Exit();
}
The main menu update does
not do much now, but later you add the capability to create and find a
session. If the user presses the Back button, you exit the sample.
To check for a single button press in many of your menus, use a helper method that you can add to your Game class.
// Helper to determine if a button was pressed but will
// not allow repeat presses over several frames
bool ButtonPressed(Buttons button)
{
// Don't process buttons when the guide is visible
if (Guide.IsVisible)
return false;
return currentGamePadState.IsButtonDown(button) &&
lastGamePadState.IsButtonUp(button);
}
The ButtonPressed method returns true if the button was pressed on this frame and does not allow for multiple button presses.
To call the draw methods for the different menu screens in the sample, add the following to your game’s Draw method.
// Switch to call the correct draw method for the
// current game state
switch (gameState)
{
case GameState.MainMenu:
MainMenuDraw();
break;
}
// Draw the current display message
if (gameMessages.Count > 0)
{
DisplayMessage currentMessage = gameMessages[0];
spriteBatch.Begin();
Vector2 stringSize = spriteFont.MeasureString(gameMessages[0].Message);
spriteBatch.DrawString(spriteFont, gameMessages[0].Message, new Vector2((1280 -
stringSize.X) / 2.0f, 500), Color.White);
spriteBatch.End();
}
To implement MainMenuDraw to display the text for the main menu, add the following method to your Game class.
// Draw method for the MainMenu GameState
private void MainMenuDraw()
{
spriteBatch.Begin();
spriteBatch.DrawString(spriteFont, "MAIN MENU", new Vector2(10, 10), Color.White);
spriteBatch.DrawString(spriteFont, "Create Session - Press A",
new Vector2(10, 50), Color.White);
spriteBatch.DrawString(spriteFont, "Find Session - Press B",
new Vector2(10, 90), Color.White);
spriteBatch.DrawString(spriteFont, "Exit - Press Back",
new Vector2(10, 130), Color.White);
spriteBatch.End();
}
The main menu text is
simple and displays the menu title and three menu options for creating a
session, finding a session, and exiting the game.
Running the sample at this point should just display the simple main menu. The A and B buttons won’t do anything yet because you have not wired them in your MainMenuUpdate method. Pressing the Back button exits the sample.
The sample should look like Figure 4.